package cn.itcast.account.service.impl;

import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountTCCService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class AccountTCCServiceImpl implements AccountTCCService {

    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountFreezeMapper accountFreezeMapper;

    /**
     * 扣减用户余额 并将扣减的金额和事务状态记录到记录表中
     *
     * @param userId 用户id
     * @param money  金额
     */
    @Override
    @Transactional
    public void tryDeduct(String userId, int money) {
        //0.获取事务ID
        String xid = RootContext.getXID();
        //0.1 为了避免业务悬挂，先判断记录表中这条业务是否已经执行过了
        AccountFreeze freeze = accountFreezeMapper.selectById(xid);
        if (freeze != null) {
            //0.1.1 如果有，就直接返回
            return;
        }
        //1.扣减可用余额 （因为金额字段在mysql中已经用unsigned注明了，扣减为负会报错，所以直接扣减即可）
        accountMapper.deduct(userId, money);
        //2.记录冻结金额和事务状态
        AccountFreeze accountFreeze = new AccountFreeze();
        //记录用户id
        accountFreeze.setUserId(userId);
        //记录金额
        accountFreeze.setFreezeMoney(money);
        //记录事务状态
        accountFreeze.setState(AccountFreeze.State.TRY);
        //记录事务id
        accountFreeze.setXid(xid);
        //插入金额事务记录表
        accountFreezeMapper.insert(accountFreeze);
    }

    /**
     * 如果事务成功，就直接删除记录即可
     *
     * @param context 上下文
     * @return
     */
    @Override
    public boolean confirm(BusinessActionContext context) {
        //1.获取事务id
        String xid = context.getXid();
        //2.根据id删除记录
        int count = accountFreezeMapper.deleteById(xid);
        if (count > 0) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 如果事务失败，就需要根据记录表，恢复可用余额
     *
     * @param context 上下文
     * @return
     */
    @Override
    public boolean cancel(BusinessActionContext context) {
        //0.1为了处理空回滚，先判断记录表中是否有try过这个事务，如果没有证明要处理空回滚
        //0.1.2查询是否有这条事务记录
        AccountFreeze freeze = accountFreezeMapper.selectById(context.getXid());
        //0.1.3为空证明没有try过这条数据，需要进行空回滚
        if (freeze == null) {
            //记录
            freeze = new AccountFreeze();
            //事务id
            freeze.setXid(context.getXid());
            //事务状态
            freeze.setState(AccountFreeze.State.CANCEL);
            freeze.setUserId((String) context.getActionContext("userId"));
            freeze.setFreezeMoney(0);
            int count = accountFreezeMapper.insert(freeze);
            if (count > 0) {
                return true;
            } else {
                return false;
            }
        }
        //0.2由于可能存在cancel失败导致多次重试cancel，所以需要幂等判断
        if (freeze.getState() == AccountFreeze.State.CANCEL) {
            //0.2.1已经cancel，无需再cancel
            return true;
        }
        //1.恢复可用余额 从上下文取出用户id和金额
        accountMapper.refund((String) context.getActionContext("userId"), (Integer) context.getActionContext("money"));
        //2.将冻结金额清零、状态改为cancel
        AccountFreeze accountFreeze = new AccountFreeze();
        accountFreeze.setXid(context.getXid());
        accountFreeze.setState(AccountFreeze.State.CANCEL);
        accountFreeze.setFreezeMoney(0);
        int count = accountFreezeMapper.updateById(accountFreeze);
        if (count > 0) {
            return true;
        } else {
            return false;
        }
    }
}
